#include "formatter.h"
#include "settings.h"

#include <QDebug>
#include <QDir>
#include <QFile>
#include <QJsonDocument>
#include <QtHttpServer/QHttpServerResponse>
#include "dcmtk/dcmdata/dcdict.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/ofstd/offile.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcuid.h"

const Formatter Formatter::s_formatter;

const Formatter &Formatter::Singleton()
{
    return s_formatter;
}

Formatter::Formatter()
{
    DcmDataDictionary &dict = dcmDataDict.wrlock();
    QString dictPath = QDir::currentPath() + "/etc/dicom.dic";
    bool loaded = dict.loadDictionary(dictPath.toLocal8Bit());
    if (!loaded) {
        qFatal() << "Load dicom dict failed: " << dictPath;
    }
    dcmDataDict.rdunlock();

    const QString dbPath = Settings::Singleton().scpDbPath();
    QDir dir(dbPath);
    if (!dir.exists()) {
        dir.mkpath(dbPath);
    }
    if (!dir.exists("lockfile")) {
        QFile lf(dbPath + "/" + "lockfile");
        if (!lf.open(QIODevice::WriteOnly)) {
            qFatal() << "Create lockfile failed: " << lf.fileName();
        }
        lf.close();
    }
}

QHttpServerResponse Formatter::processHttpRequest(const QByteArray &data) const {
    QJsonObject resp;
    QJsonParseError err;
    QJsonDocument doc = QJsonDocument::fromJson(data, &err);
    if (err.error != QJsonParseError::ParseError::NoError) {
        resp.insert("success", false);
        resp.insert("message", "Json parse failed: " + err.errorString());
        return QHttpServerResponse(resp, QHttpServerResponse::StatusCode::BadRequest);
    }

    QJsonObject json = doc.object();
    if (json.empty()) {
        resp.insert("success", false);
        resp.insert("message", "Empty request data.");
        return QHttpServerResponse(resp, QHttpServerResponse::StatusCode::BadRequest);
    }

    resp = processJson(json);
    const bool ok = resp.value("success").toBool();

    QHttpServerResponse rs = QHttpServerResponse(resp, ok ? QHttpServerResponse::StatusCode::Ok : QHttpServerResponse::StatusCode::InternalServerError);
    rs.setHeader("Access-Control-Allow-Origin","*");
    rs.setHeader("Access-Control-Allow-Methods","*");
    rs.setHeader("Access-Control-Allow-Headers","*");
    return rs;
}

QJsonObject Formatter::processJson(const QJsonObject &json) const
{
    QJsonObject resp;
    resp.insert("success", true);
    resp.insert("message", "ok");

    DcmFileFormat ff;
    DcmDataset *dset = ff.getDataset();
    QString uid;

    QString val = json.value("SpecificCharacterSet").toString();
    if (val.isEmpty()) {
        val = "GB18030";
    }
    dset->putAndInsertString(DCM_SpecificCharacterSet, val.toLocal8Bit());

    uid = json.value("StudyInstanceUID").toString();
    if (uid.isEmpty()) {
        char uidBuf[100];
        dcmGenerateUniqueIdentifier(uidBuf, SITE_STUDY_UID_ROOT);
        uid = uidBuf;
    }
    dset->putAndInsertString(DCM_StudyInstanceUID, uid.toLocal8Bit());

    val = json.value("AccessionNumber").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_AccessionNumber, val.toLocal8Bit());
    }

    val = json.value("PatientName").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_PatientName, val.toLocal8Bit());
    }

    val = json.value("PatientID").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_PatientID, val.toLocal8Bit());
    }

    val = json.value("PatientBirthDate").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_PatientBirthDate, val.toLocal8Bit());
    }

    val = json.value("PatientSex").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_PatientSex, val.toLocal8Bit());
    }

    val = json.value("MedicalAlerts").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_MedicalAlerts, val.toLocal8Bit());
    }

    val = json.value("Allergies").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_Allergies, val.toLocal8Bit());
    }

    val = json.value("RequestingPhysician").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_RequestingPhysician, val.toLocal8Bit());
    }

    val = json.value("RequestedProcedureDescription").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_RequestedProcedureDescription, val.toLocal8Bit());
    }

    val = json.value("RequestedProcedureID").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_RequestedProcedureID, val.toLocal8Bit());
    }

    val = json.value("RequestedProcedurePriority").toString();
    if (!val.isEmpty()) {
        dset->putAndInsertString(DCM_RequestedProcedurePriority, val.toLocal8Bit());
    }

    QJsonObject seq = json.value("ScheduledProcedureStepSequence").toObject();
    if (!seq.isEmpty()) {
        DcmItem *item = NULL;
        OFCondition cond = dset->findOrCreateSequenceItem(DCM_ScheduledProcedureStepSequence, item);
        if (cond.good() || item) {
            val = seq.value("Modality").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_Modality, val.toLocal8Bit());
            }

            val = seq.value("RequestedContrastAgent").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_RequestedContrastAgent, val.toLocal8Bit());
            }

            val = seq.value("ScheduledStationAETitle").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledStationAETitle, val.toLocal8Bit());
            }

            val = seq.value("ScheduledProcedureStepStartDate").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledProcedureStepStartDate, val.toLocal8Bit());
            }

            val = seq.value("ScheduledProcedureStepStartTime").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledProcedureStepStartTime, val.toLocal8Bit());
            }

            val = seq.value("ScheduledProcedureStepEndDate").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledProcedureStepEndDate, val.toLocal8Bit());
            }

            val = seq.value("ScheduledProcedureStepEndTime").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledProcedureStepEndTime, val.toLocal8Bit());
            }

            val = seq.value("ScheduledPerformingPhysicianName").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledPerformingPhysicianName, val.toLocal8Bit());
            }

            val = seq.value("ScheduledProcedureStepDescription").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledProcedureStepDescription, val.toLocal8Bit());
            }

            val = seq.value("ScheduledProcedureStepID").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledProcedureStepID, val.toLocal8Bit());
            }

            val = seq.value("ScheduledStationName").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledStationName, val.toLocal8Bit());
            }

            val = seq.value("ScheduledProcedureStepLocation").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_ScheduledProcedureStepLocation, val.toLocal8Bit());
            }

            val = seq.value("PreMedication").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_PreMedication, val.toLocal8Bit());
            }

            val = seq.value("CommentsOnTheScheduledProcedureStep").toString();
            if (!val.isEmpty()) {
                item->putAndInsertString(DCM_CommentsOnTheScheduledProcedureStep, val.toLocal8Bit());
            }
        } else {
            qCritical() << "Insert sequence item failed: " << cond.text();
        }
    }

    const Settings &s = Settings::Singleton();
    QString filename = s.scpDbPath() + "/" + s.scpAet() + "/" + uid + ".wl";
    OFFilename fn(filename.toLocal8Bit());
    OFCondition cond = ff.saveFile(fn, EXS_LittleEndianImplicit, EET_UndefinedLength, EGL_withoutGL, EPD_withoutPadding, 0, 0, EWM_dataset);
    if (cond.bad()) {
        qCritical() << "Save worklist file failed: " << cond.text() << "File name: " << filename;
        resp.insert("success", false);
        resp.insert("message", cond.text());
    }

    return resp;
}
